package ovt;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;

/**
 * 用户请求处理线程
 */
public class EchoServerThread extends Thread {

	private Logger logger = Logger.getLogger(EchoServerThread.class);
	ConfResolution conf = new ConfResolution();
	private HashMap<Socket, ClientInformation> alclient = new HashMap<Socket, ClientInformation>();
	private Queue<SocketChannel> clientqueue = new ConcurrentLinkedQueue<SocketChannel>();
	private Queue<SocketChannel> client_not_completed = new ConcurrentLinkedQueue<SocketChannel>();
	private int TIME_OUT = 10;
	Selector selector;
	private boolean canaddclient = true;
	private int buffersize = ConfResolution.getSocketBufferSize();
	private boolean isinterrupted = false;
	public AtomicInteger client_count = new AtomicInteger();// 往线程中添加用户通道时自加1

	public EchoServerThread() {

		try {
			this.selector = Selector.open();
			client_count.set(0);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void run() {
		SocketChannel sc = null;
		try {
			while (!Thread.interrupted() && (!isinterrupted)) {
				if (selector.select(TIME_OUT) > 0) {
					ByteBuffer echoBuffer = ByteBuffer.allocate(buffersize);
					Set<SelectionKey> selectionKeys = selector.selectedKeys();
					Iterator<SelectionKey> iter = selectionKeys.iterator();
					while (iter.hasNext()) {
						byte[] msglenbyte = new byte[4];
						byte[] codebyte = new byte[4];
						int codelength = 0;
						SelectionKey key = iter.next();
						iter.remove();
						if(key.isReadable()){
							sc = (SocketChannel) key.channel();
							ClientInformation cl = new ClientInformation();
							if (!alclient.containsKey(sc.socket())) {
								alclient.put(sc.socket(), cl);
							} else {
								cl = alclient.get(sc.socket());
							}
							int a = 0;
							try {
								echoBuffer.clear();
								// 取消息总长度
								a = sc.read(echoBuffer);
								if (a == -1) {
									logger.error("已达流的末尾；read返回值为-1");
									alclient.remove(sc.socket());
									client_count.getAndDecrement();
									key.cancel();
									if (key.channel() != null) {
										sc.socket().close();
										key.channel().close();
									}
								}
								echoBuffer.flip();
								echoBuffer.get(msglenbyte, 0, 4);
								codelength = FormatHandler.byte2int(msglenbyte);
								// 读取指令
								logger.info("指令长度为：" + codelength);
								codebyte = new byte[codelength];
								echoBuffer.get(codebyte, 0, codelength);
								logger.info("接收到的指令为："+new String(codebyte));
							} catch (Exception e) {
								sc.socket().close();
								e.printStackTrace();
								logger.error("Client"
										+ sc.socket().getInetAddress()
												.getHostAddress() + "断开，处理结束");
								alclient.remove(sc.socket());
								client_count.getAndDecrement();
								key.cancel();
								if (key.channel() != null) {
									sc.socket().close();
									key.channel().close();
								}
							}

							if (a > 0) {
								if (new String(codebyte).startsWith("START")) {
									try {
										startProcedure(echoBuffer, sc, cl);
									} catch (Exception e) {
										e.printStackTrace();
										logger.error("start阶段捕捉到IOEception，出错用户为："
												+ sc.socket().getInetAddress()
														.getHostAddress()
												+ ";断开与client的连接");
										logger.error("将用户通道从未完成列表中移除");
										client_not_completed.remove(sc);// 将用户从未完成列表中删除
										alclient.remove(sc.socket());// 将用户从全部用户中删除
										client_count.getAndDecrement();// 用户个数减一
										key.cancel();
										if (key.channel() != null) {
											logger.error("断开与client的连接");
											sc.socket().close();
											key.channel().close();
											logger.error("START STAGE COMPLETED.");
										}
									}
									logger.info("START STAGE COMPLETED.");
								} else if (new String(codebyte)
										.startsWith("ENTER")) {
									logger.info("ENTER STAGE START.");
									try {
										sendThumbnail_new(echoBuffer, sc, cl);
									} catch (Exception e) {
										e.printStackTrace();
										logger.error("enter阶段捕捉到IOEception，出错用户为："
												+ sc.socket().getInetAddress()
														.getHostAddress()
												+ ";断开与client的连接");
										logger.error("将用户通道从未完成列表中移除");
										client_not_completed.remove(sc);
										alclient.remove(sc.socket());
										client_count.getAndDecrement();
										key.cancel();
										if (key.channel() != null) {
											logger.error("断开与client的连接");
											sc.socket().close();
											key.channel().close();
											logger.error("ENTER STAGE COMPLETED.");
										}
									}
									logger.info("ENTER STAGE COMPLETED.");
								} else if (new String(codebyte)
										.equals("DOWNLOAD")) {
									logger.info("DOWNLOAD STAGE START.");
									try {
										sendDownloadFile(echoBuffer, sc, cl);
									} catch (Exception e) {
										e.printStackTrace();
										logger.error("download阶段捕捉到IOEception，出错用户为"
												+ sc.socket().getInetAddress()
														.getHostAddress()
												+ ";断开与client的连接");
										logger.error("将用户通道从未完成列表中移除");
										client_not_completed.remove(sc);
										alclient.remove(sc.socket());
										client_count.getAndDecrement();
										key.cancel();
										if (key.channel() != null) {
											logger.error("断开与client的连接");
											sc.socket().close();
											key.channel().close();
											logger.error("DOWNLOAD STAGE COMPLETED");
										}
									}
									logger.info("DOWNLOAD STAGE COMPLETED");
								} else if (new String(codebyte)
										.startsWith("UPLOAD")) {
									logger.info("client UPLOAD material to server");
									saveUserMaterial(echoBuffer, sc, cl);
									// TODO 添加try-catch块
								} else if (new String(codebyte)
										.startsWith("SHARE")) {
									logger.info("用户分享指令");
									shareMaterial(echoBuffer, sc, cl);
								} else if (new String(codebyte)
										.startsWith("CHECKXML")) {
									logger.info("CHECK the xml version with client");
									try {
										checkXMLVersion(echoBuffer, sc, cl);
									} catch (Exception e) {
										e.printStackTrace();
										logger.error("check xml阶段捕捉到IOEception，出错用户为："
												+ sc.socket().getInetAddress()
														.getHostAddress()
												+ "；断开与client的连接");
										logger.error("将用户通道从未完成列表中移除");
										client_not_completed.remove(sc);
										alclient.remove(sc.socket());
										client_count.getAndDecrement();
										key.cancel();
										if (key.channel() != null) {
											logger.error("断开与client的连接");
											sc.socket().close();
											key.channel().close();
											logger.error("CHECK STAGE COMPLETED.");
										}
									}
									logger.info("CHECK STAGE COMPLETED.");
								} else if (new String(codebyte)
										.startsWith("EXIT")) {
									try {
										exitProcedure(echoBuffer, sc, cl);
									} catch (Exception e) {
										e.printStackTrace();
										logger.error("exit阶段捕捉到IOEception，出错用户为："
												+ sc.socket().getInetAddress()
														.getHostAddress()
												+ "；断开与client的连接");
										logger.error("将用户通道从未完成列表中移除");
										client_not_completed.remove(sc);
										alclient.remove(sc.socket());
										client_count.getAndDecrement();
										key.cancel();
										if (key.channel() != null) {
											logger.error("断开与client的连接");
											sc.socket().close();
											key.channel().close();
											logger.error("EXIT STAGE COMPLETED.");
										}
									} finally {
										logger.info("将用户通道从未完成列表中移除");
										client_not_completed.remove(sc);
										alclient.remove(sc.socket());
										client_count.getAndDecrement();
										key.cancel();
										if (key.channel() != null) {
											logger.info("断开与client的连接");
											sc.socket().close();
											key.channel().close();
											logger.info("EXIT STAGE COMPLETED.");
										}
									}
								}
							}
						}
					}
				} else {
					SocketChannel socketchannel = null;
					synchronized (clientqueue) {
						while ((socketchannel = clientqueue.poll()) != null) {
							// 将这个用户注册本线程的socketchannel的同时，将其加入到未完成列表；
							socketchannel.register(selector,
									SelectionKey.OP_READ|SelectionKey.OP_WRITE);
							client_not_completed.add(socketchannel);
						}
					}
				}
			}
		} catch (Exception e) {
			logger.info("用户请求处理线程即将关闭，线程ID为：" + Thread.currentThread().getId());
			logger.info("该线程中有" + client_not_completed.size() + "个用户正在排队");
			if (client_not_completed.contains(sc)) {
				client_not_completed.remove(sc);
			}
			if (client_not_completed.size() != 0) {
				Main.threadmain.forDeadThread(client_not_completed);
			}
			Thread.currentThread().interrupt();
			isinterrupted = true;
			e.printStackTrace();
		}
	}

	private void startProcedure(ByteBuffer bb, SocketChannel sc,
			ClientInformation cl) throws IOException {
		// XML文件长度（4字节）+XML文件
		FileHandler filep = new FileHandler();
		String clientname = sc.socket().getInetAddress().getHostAddress();
		int port = sc.socket().getPort();
		cl.setClientname(clientname);
		cl.setPort(port);

		logger.info("Receive resource.xml from Client: "
				+ sc.socket().getInetAddress().getHostAddress());
		byte[] buf = new byte[4];
		bb.get(buf, 0, 4);
		int xmlfilesize = FormatHandler.byte2int(buf);
		if (xmlfilesize == 0) {
			logger.error("Client resource file size is 0! Maybe the connection is interrupted.");
			return;
		}
		String clientxmlfilepath = ConfResolution.getClientDirectory() + "\\"
				+ clientname + "_now.xml";
		cl.setClientxmlfilepath(clientxmlfilepath);
		logger.debug("保存客户端material资源文件。客户端文件大小为：" + xmlfilesize);
		filep.readAndWrite0(bb, clientxmlfilepath, xmlfilesize, sc);
		logger.info("保存客户端material资源文件成功。");
		// 调用XmlOperator类中的方法，对两个resource.xml文件进行比较；
		// 考虑到客户端xml文件错误的情况
		boolean ret = XmlHandler.checkXMLValid(clientxmlfilepath);
		String updatemessage = null;
		if (ret == true) {
			// 如果xml文件是正确的，将上次保存的xml文件删掉，将这次收到的重命名备用
			File f = new File(ConfResolution.getClientDirectory() + clientname
					+ "_last.xml");
			logger.debug("客户端XML文件正确，删除Server端保存的上次XML文件 "
					+ f.getAbsolutePath());
			f.delete();
			logger.debug("将用户本次传入的xml文件重命名为_last.xml");
			new File(ConfResolution.getClientDirectory() + clientname
					+ "_now.xml").renameTo(f);
		} else {
			// 如果xml文件是错误的，删掉这次读到的
			logger.debug("客户端XML文件出错，使用Server端上次保存的XML文件，删除客户端此次的XML文件："
					+ clientxmlfilepath);
			new File(clientxmlfilepath).delete();
		}
		clientxmlfilepath = ConfResolution.getClientDirectory() + clientname
				+ "_last.xml";
		cl.setClientxmlfilepath(clientxmlfilepath);
		updatemessage = XmlHandler.getUpdateMessageToClient(clientxmlfilepath);

		if (updatemessage == null) {
			logger.info("返回消息长度为0");
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			int sendcount = sc.write(bb);
			while (sendcount < 4) {
				sendcount += sc.write(bb);
			}
			return;
		}

		HashMap<String, HashSet<String>> lastdownloaded = XmlHandler
				.getDownloadMap(cl.getClientxmlfilepath());
		cl.setLastdownloaded(lastdownloaded);
		cl.setUpdatemessage(updatemessage);
		logger.info("返回消息为："+updatemessage);
		bb.clear();
		bb.put(FormatHandler.int2byte(updatemessage.length()));
		bb.flip();
		int sendcount = sc.write(bb);
		while (sendcount < 4) {
			sendcount += sc.write(bb);
		}

		bb.clear();
		bb.put(updatemessage.getBytes());
		bb.flip();
		sendcount = sc.write(bb);
		while (sendcount < updatemessage.length()) {
			sendcount += sc.write(bb);
		}
		return;
	}

	/**
	 * 由于Client端传入的素材名字不全，这个方法用于补全素材名称，返回值为素材种类全称。在run()中前面加上material路径，后面加上素材名字
	 * ，拼出素材全路径
	 * 
	 * @param ss
	 * @return
	 */
	private String findFullPathFromDirectory(String ss) {
		Main.xmlfilerwlock.readLock().lock();
		HashMap<String, Integer> hs = XmlHandler.hm_server;
		Main.xmlfilerwlock.readLock().unlock();
		Iterator i = hs.entrySet().iterator();
		while (i.hasNext()) {
			Map.Entry<String, Integer> entry = (Map.Entry<String, Integer>) i
					.next();
			String temp = entry.getKey();
			if (temp.contains(ss)) {
				return temp;
			}
		}
		return null;
	}

	/**
	 * 由于有的mask文件后缀名与素材文件的后缀名不符，这个函数用于根据mask文件夹以及文件名字查找mask文件，主要是确定后缀；
	 * 
	 * @param maskfilepath
	 * @return
	 */
	private String findMaskFileName(String maskfilepath) {
		if (maskfilepath == null)
			return null;
		maskfilepath = maskfilepath.replaceAll("\\\\\\\\", "\\\\");// 所有双反斜杠都换成单反斜杠
		String maskfilefullname = null;
		int index = maskfilepath.lastIndexOf("\\");
		int index1 = maskfilepath.lastIndexOf(".");
		File maskdirectory = new File(maskfilepath.substring(0, index));
		String maskfilename = maskfilepath.substring(index + 1, index1);
		if (maskdirectory.isDirectory()) {
			File[] maskfiles = maskdirectory.listFiles();
			for (int i = 0; i < maskfiles.length; i++) {
				if (maskfiles[i].getName().startsWith(maskfilename))
					maskfilefullname = maskfiles[i].getAbsolutePath();
			}
		}
		if (maskfilefullname != null)
			logger.debug("The Server cannot find corresponding maskfile, but find the most similarity: "
					+ maskfilefullname);
		else {
			logger.debug("Can't find corresponding maskfile");
		}
		return maskfilefullname;
	}

	/**
	 * 接收客户端传入的server端xml文件版本号，如果版本号与当前server端不一致，需返回新的xml文件，如一致，则返回0；
	 * 
	 * @param bb
	 * @param sc
	 * @param cl
	 */
	private void checkXMLVersion(ByteBuffer bb, SocketChannel sc,
			ClientInformation cl) throws IOException {
		int versionlength = 0;
		byte[] buf = new byte[4];
		bb.get(buf, 0, 4);
		versionlength = FormatHandler.byte2int(buf);
		buf = new byte[versionlength];
		bb.get(buf, 0, versionlength);
		String version = new String(buf);
		logger.info("客户端xml文件的版本号为：" + version + ";服务器端xml文件的版本号为："
				+ String.valueOf(ConfResolution.getServerXMLVersion()));
		if (version
				.equals(String.valueOf(ConfResolution.getServerXMLVersion()))) {
			logger.info("XML file of Client " + cl.getClientname()
					+ " is the latest");
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			int sendlength = 0;
			sendlength = sc.write(bb);
			while (sendlength < 4) {
				sendlength += sc.write(bb);
			}
		} else {
			File file = new File(ConfResolution.getServerXMLFilePath());
			if (!file.exists()) {
				logger.error("需要将服务器版本号发送给客户端，但是服务器端xml文件不存在；");
				// 发送一个0给客户端；
				bb.clear();
				bb.put(FormatHandler.int2byte(0));
				bb.flip();
				int sendlength = 0;
				sendlength = sc.write(bb);
				while (sendlength < 4) {
					sendlength += sc.write(bb);
				}
				// 返回；
				return;
			}
			logger.info("Send Latest XML File to Client:" + cl.getClientname());
			// 从准备发送xml文件给客户端就给xml文件加读锁
			Main.xmlfilerwlock.readLock().lock();
			FileInputStream buf_in = null;
			try {
				bb.clear();
				bb.put(FormatHandler.int2byte((int) file.length()));
				bb.flip();
				int sendlength = 0;
				sendlength = sc.write(bb);
				while (sendlength < 4) {
					sendlength += sc.write(bb);
				}
				buf_in = new FileInputStream(file);
				byte[] buffer = new byte[buffersize];
				int len = -1;
				while ((len = buf_in.read(buffer, 0, buffer.length)) != -1) {
					bb.clear();
					bb.put(buffer, 0, len);
					bb.flip();
					int sendcount = sc.write(bb);
					while (sendcount < len)
						sendcount += sc.write(bb);
				}
				buf_in.close();
			} catch (IOException e) {
				try {
					if (buf_in != null)
						buf_in.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			} finally {
				Main.xmlfilerwlock.readLock().unlock();
			}
		}
	}

	private void sendThumbnail_new(ByteBuffer bb, SocketChannel sc,// 客户端命令格式为：pip+1+6
			ClientInformation cl) throws IOException {
		long starttime = System.nanoTime();
		String file_direc_name;
		String clientname = cl.getClientname();
		byte[] buf = new byte[4];
		bb.get(buf, 0, 4);
		int requestlength = FormatHandler.byte2int(buf);
		buf = new byte[requestlength];
		bb.get(buf, 0, requestlength);
		logger.info("客户端命令为：" + new String(buf));
		String[] info = new String(buf).split("\\+");
		// 如果客户端命令出错，向客户端返回0，而如果数据正确的话，将会返回需要发送给客户端的缩略图的个数；
		if (info.length != 3) {
			logger.error("客户端请求缩略图的语句为：" + new String(buf) + ",格式有误，即将返回0");
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			int sendlength;
			try {
				sendlength = sc.write(bb);
				while (sendlength < 4) {
					sendlength += sc.write(bb);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			return;
		}

		String directoryname = info[0];// decorator\glasses
		int pageindex = Integer.parseInt(info[1]);// 第几页
		int pagehas = Integer.parseInt(info[2]);// 每页有几个
		int startindex = (pageindex - 1) * pagehas + 1;
		int endindex = pageindex * pagehas;
		//
		// 记录本次浏览的信息
		file_direc_name = findFullPathFromDirectory(directoryname);
		HashSet<String> temp = cl.getThistimebrowsed();
		temp.add(file_direc_name);
		cl.setThistimebrowsed(temp);
		// 得到缩略图路径
		File thumbnaildirectory = new File(new File(
				ConfResolution.getMaterialDirectory() + file_direc_name)
				.getParentFile().getAbsoluteFile()
				+ "\\Thumbnail\\");
		if (!thumbnaildirectory.exists()) {
			logger.info("This material directory has been deleted from server");
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			int sendlength = sc.write(bb);
			while (sendlength < 4) {
				sendlength += sc.write(bb);
			}
			return;
		}
		logger.info("本次客户端共请求" + pagehas + "个缩略图文件" + ";缩略图文件夹为："
				+ thumbnaildirectory.getAbsolutePath());

		// 从这个list中剔除用户已经下载过的素材；
		ArrayList<String> tobedownloadedlist = new ArrayList<String>();
		if (MatLibInfomation.hm_thumbnail != null
				&& MatLibInfomation.hm_thumbnail.containsKey(ConfResolution
						.getMaterialDirectory() + file_direc_name)) {
			tobedownloadedlist = new MatLibInfomation().removeDowbloadedFiles(
					MatLibInfomation.getSequencedThumbnail(ConfResolution
							.getMaterialDirectory() + file_direc_name), cl
							.getLastdownloaded().get(file_direc_name));
		}
		logger.debug("服务器端共有" + tobedownloadedlist.size()
				+ "个客户端未下载过的缩略图，这些缩略图按照时间新旧顺序的排列为："
				+ tobedownloadedlist.toString());
		if (tobedownloadedlist == null || tobedownloadedlist.size() == 0) {
			logger.info("可供用户下载的资源数目不正确：tobedownloadedlist="
					+ tobedownloadedlist);
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			int sendlength;
			sendlength = sc.write(bb);
			while (sendlength < 4) {
				sendlength += sc.write(bb);
			}
			return;
		}
		logger.info("客户端请求下载其中的第" + startindex + "到第" + endindex + "个");
		if (endindex > tobedownloadedlist.size()) {
			logger.info("服务器端共有" + tobedownloadedlist.size()
					+ "个缩略图文件供客户端下载，剩余可下载的个数为"
					+ (tobedownloadedlist.size() - startindex + 1));
			endindex = tobedownloadedlist.size();
		}
		int sendnumber = endindex - startindex + 1;
		if (sendnumber <= 0 || startindex <= 0) {
			logger.error("sendnumber（一共将发送的文件个数）不正确或者startindex（请求起始位置）不正确：可供下载的素材个数为：0");
			logger.error("sendnumber = " + sendnumber + ",startindex = "
					+ startindex);
			sendnumber = 0;
		}

		// 向客户端发送文件个数信息
		bb.clear();
		bb.put(FormatHandler.int2byte(sendnumber));
		bb.flip();
		int sendlength = sc.write(bb);
		while (sendlength < 4)
			sendlength += sc.write(bb);
		if (sendnumber == 0) {
			return;
		}
		// 开始发送缩略图文件
		BufferedInputStream buf_in = null;
		try {
			Main.xmlfilerwlock.readLock().lock();// 发送缩略图时，不允许用户进行素材库的修改；
			for (int i = startindex - 1; i < endindex; i++) {
				String materialpath = tobedownloadedlist.get(i);
				String onlyfilename = materialpath.substring(
						materialpath.lastIndexOf("\\") + 1,
						materialpath.lastIndexOf("."));
				// 校验文件是否存在；
				String thumbnailfilepath = thumbnaildirectory + "\\"
						+ onlyfilename + ".png";// 所以，缩略图文件的后缀必须是png
				// 发送文件
				File file = new File(thumbnailfilepath);

				buf_in = new BufferedInputStream(new FileInputStream(file));
				bb.clear();
				bb.put(FormatHandler.int2byte(file.getName().getBytes().length));// 发送文件名长度

				logger.debug("发送的文件名字长度为：" + file.getName() + " 发送的文件长度为："
						+ FormatHandler.long2byte(file.length()));
				bb.put(FormatHandler.long2byte(file.length()));// 发送文件长度
				bb.put(file.getName().getBytes("GB2312"));// 发送文件名称
				bb.flip();
				int length = 12 + file.getName().length();
				int sendcount = sc.write(bb);
				while (sendcount < length) {
					sendcount += sc.write(bb);
				}
				byte[] buffer = new byte[buffersize];
				int len = -1;
				while ((len = buf_in.read(buffer, 0, buffer.length)) != -1) {
					bb.clear();
					bb.put(buffer, 0, len);
					bb.flip();
					sendcount = sc.write(bb);
					while (sendcount < len) {
						sendcount = sendcount + sc.write(bb);
					}
				}
				logger.info("Send file " + file.getName()
						+ " Completed,Client name is:" + clientname);
			}
			buf_in.close();
		} catch (IOException e) {// 这一段try语句如果出现异常被抛出到run()中，就不能及时关闭xml文件读锁，所以必须在此处catch异常；
			try {
				if (buf_in != null) {
					buf_in.close();
				}
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		} finally {
			Main.xmlfilerwlock.readLock().unlock();// 所有的素材文件锁都放到finally里一起解锁
		}

		long endtime = System.nanoTime();
		logger.info("本次发送缩略图耗时：" + (endtime - starttime) / 1000 / 1000 + "毫秒");
	}

	/**
	 * @deprecated 发送素材文件缩略图到Client端
	 *             这个方法中按照时间先后顺序将每个素材文件夹中的文件排序了，file_direc_name用来取得已经排好序的素材文件
	 * 
	 * @param file_direc_name
	 * @throws IOException
	 */
	private void sendThumbnail(ByteBuffer bb, SocketChannel sc,
			ClientInformation cl) throws IOException, InterruptedException {// frame\florid
		String file_direc_name;
		String clientname = cl.getClientname();
		// map中保存的是按照时间先后排序的文件
		// Map<Long, String> map = xmlop.getFileList(file_direc_name);
		bb.get();
		int index = 0;
		byte split = 59;
		while (bb.get(bb.position() + index) != split) {
			// bb.get();
			// System.out.println(bb.get(bb.position()));
			index++;
			continue;
		}
		// logger.info("2");
		byte[] buf = new byte[index];
		bb.get(buf, 0, index);
		String directoryname = new String(buf);
		file_direc_name = findFullPathFromDirectory(directoryname);

		// 记录本次浏览的信息
		HashSet<String> temp = cl.getThistimebrowsed();
		temp.add(file_direc_name);
		cl.setThistimebrowsed(temp);
		//
		BufferedInputStream buf_in = null;
		// File directory = new File(ConfResolution.getThumbnailPath()
		// + file_direc_name);
		File directory = new File(new File(
				ConfResolution.getMaterialDirectory() + file_direc_name)
				.getParentFile().getAbsoluteFile()
				+ "\\Thumbnail\\");
		if (!directory.exists()) {
			logger.debug("This material directory has been deleted from server");
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			int sendlength = sc.write(bb);
			while (sendlength < 4) {
				sendlength += sc.write(bb);
			}
			return;
		}
		try {
			File[] files = directory.listFiles();
			HashSet<String> tobedownloaded = new HashSet<String>();
			for (File file : files) {
				String tobedownloadfilepath = file.getAbsolutePath();
				String tobedownloadfile = tobedownloadfilepath.substring(
						tobedownloadfilepath.lastIndexOf("\\") + 1,
						tobedownloadfilepath.lastIndexOf("."));
				HashSet<String> hs = new HashSet<String>();
				// hs = this.lastdownloaded.get(file_direc_name);
				hs = cl.getLastdownloaded().get(file_direc_name);
				if (!hs.contains(tobedownloadfile)) {
					// 看每个即将发送的缩略图是不是有mask文件和素材文件，都有了才发送给客户端
					if (!FileHandler.isThumbnailValid(tobedownloadfilepath))
						continue;
					logger.debug("本次将发送的缩略图全路径：" + tobedownloadfilepath);
					tobedownloaded.add(tobedownloadfilepath);
				}
			}
			logger.debug("enter " + file_direc_name
					+ " The number of pictures is:" + tobedownloaded.size()
					+ ",and client name is:" + cl.getClientname());
			bb.clear();
			bb.put(FormatHandler.int2byte(tobedownloaded.size()));
			bb.flip();
			int sendlength = sc.write(bb);
			while (sendlength < 4) {
				sendlength += sc.write(bb);
			}
			// Iterator<Entry<Long, String>> i = map.entrySet().iterator();
			// while (i.hasNext()) {
			// for (File file : files) {
			for (Iterator<String> i = tobedownloaded.iterator(); i.hasNext();) {
				// Map.Entry<Long, String> entry = (Map.Entry<Long, String>)
				// i
				// .next();
				// File file = new File(this.conf.getthumbnailpath()
				// + file_direc_name + "\\" + entry.getValue());
				File file = new File(i.next().toString());

				buf_in = new BufferedInputStream(new FileInputStream(file));
				bb.clear();
				bb.put(FormatHandler.int2byte(file.getName().getBytes().length));// 发送文件名长度

				bb.put(FormatHandler.long2byte(file.length()));// 发送文件长度
				bb.put(file.getName().getBytes("GB2312"));// 发送文件名称
				bb.flip();
				int length = 12 + file.getName().length();
				int sendcount = sc.write(bb);
				while (sendcount < length) {
					sendcount += sc.write(bb);
				}
				// sleep(20);
				byte[] buffer = new byte[buffersize];
				int len = -1;
				while ((len = buf_in.read(buffer, 0, buffer.length)) != -1) {
					bb.clear();
					bb.put(buffer, 0, len);
					bb.flip();
					sendcount = sc.write(bb);
					while (sendcount < len) {
						sendcount = sendcount + sc.write(bb);
					}
				}
				buf_in.close();
				logger.debug("Send file " + file.getName()
						+ " Completed,Client name is:" + clientname);
			}
			logger.debug("Send " + tobedownloaded.size()
					+ " Files Completed!Client name is:" + clientname);
		} catch (FileNotFoundException e) {
			logger.error(e.getMessage());
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			int sendlength = sc.write(bb);
			while (sendlength < 4) {
				sendlength += sc.write(bb);
			}
			alclient.remove(sc.socket());
			sc.socket().close();
			sc.close();
		} catch (IOException e) {
			e.printStackTrace();
			alclient.remove(sc.socket());
			sc.socket().close();
			sc.close();
		} catch (NullPointerException e) {
			alclient.remove(sc.socket());
			sc.socket().close();
			sc.close();
			logger.error(e.getMessage());
			e.printStackTrace();
		}
		try {
			if (buf_in != null) {
				buf_in.close();
			}
		} catch (IOException e) {
			logger.error("IOException in Class EchoThread and Function sendThunmnail()");
			logger.error(e.getMessage());
			e.printStackTrace();
		}
	}

	/**
	 * 将用户请求的要下载的文件发送给Client端
	 * 
	 * @param filename
	 *            参数来自run方法
	 * @throws IOException
	 * @throws InterruptedException
	 */
	private void sendDownloadFile(ByteBuffer bb, SocketChannel sc,
			ClientInformation cl) throws Exception {
		// 要下载的文件名长度（4字节）+要下载的文件名
		long starttime = System.nanoTime();
		ByteBuffer bytebufferwrite = ByteBuffer.allocate(buffersize);
		
		byte[] msglenbyte = new byte[4];
		int remaincount = bb.limit() - bb.position();
		while(remaincount<4){
			bb.position(bb.limit());
			int a = sc.read(bb);
			remaincount+=a;
			if(a!=0)
				System.out.println(a);
		}
		bb.position(12);
		bb.get(msglenbyte, 0, 4);
		byte[] buf = new byte[FormatHandler.byte2int(msglenbyte)];
		int templength = FormatHandler.byte2int(msglenbyte);
		logger.info("要下载的文件名称长度为："+templength);
		logger.info("准备从缓冲区中读取要下载的文件名称；");
		logger.info("缓冲区的limit为："+bb.limit());
		logger.info("缓冲区的position为："+bb.position());
		logger.info("现在缓冲区中还剩"+(bb.limit() - bb.position())+"个数据");
		remaincount = bb.limit() - bb.position();
		while (remaincount < templength)
		{
			bb.position(bb.limit());
			int a = sc.read(bb);
			remaincount += a;
			if(a!=0) logger.info("a = "+a);
		}
		bb.position(16);
		bb.get(buf, 0, templength);
		bb.clear();
		logger.info("要下载的文件长度为：" + FormatHandler.byte2int(msglenbyte));
		String temp1 = new String(buf);
		logger.info("下载文件：" + temp1);
		
		
		// 下载指令：glasses\object\108_296_75.jpg
		String directoryname = temp1.substring(0, temp1.indexOf("\\"));
		String filenameonly = temp1.substring(temp1.lastIndexOf("\\") + 1);
		directoryname = findFullPathFromDirectory(directoryname);
		String filename = ConfResolution.getMaterialDirectory() + directoryname
				+ "\\" + filenameonly;

		// 组装mask文件的全路径
		logger.debug("要下载的文件名字为：" + filename);
		// D:\Workspace\CheckSocket\images\pip\object\202_57_546_401_31_-1.jpg
		String maskdirectory = new File(filename).getParentFile()
				.getParentFile().getAbsolutePath()
				+ "\\mask\\";
		String maskfilename = filename.substring(
				filename.lastIndexOf("\\") + 1, filename.lastIndexOf("."))
				+ "_mask.jpg";
		maskfilename = maskdirectory + maskfilename;
		logger.debug("所请求的mask文件的名字(未查找之前，jpg格式的。)为：" + maskfilename.toString());
		// 得到mask文件全路径，组装结束
		try {
			BufferedInputStream buf_in = null;
			// 开始读取文件名信息以及文件信息，锁定素材库；
			Main.xmlfilerwlock.readLock().lock();
			File maskfile = new File(maskfilename.toString());
			// 如果mask文件不存在，则调用findMaskFileName方法查找mask文件；
			if (!maskfile.exists()) {
				String maskfilenametrue = findMaskFileName(maskfilename
						.toString());
				if (maskfilenametrue == null) {// 如果mask文件不存在，向客户端发回0，退出下载过程，等待下一条指令
					logger.info("所请求的mask文件" + maskfilenametrue + "不存在");
				} else
					maskfile = new File(findMaskFileName(maskfilenametrue));
			}
			// 如果mask文件或者素材文件有一个不存在，则向客户端返回0，退出下载过程，等待下一条指令；
			File file = new File(filename);
			// 如果File变量是null的话，调用exists方法会报错；
			
			if (maskfile == null || file == null || (!maskfile.exists())
					|| (!file.exists())) {
				bytebufferwrite.clear();
				bytebufferwrite.put(FormatHandler.int2byte(0));
				bytebufferwrite.flip();
				int sendlength = sc.write(bytebufferwrite);
				while (sendlength < 4) {
					sendlength += sc.write(bytebufferwrite);
				}
				// Main.xmlfilerwlock.readLock().unlock();// 不能两次解锁，会报异常；
				return;
			}
			logger.debug("Send the picture and the mask file to Client:"
					+ cl.getClientname());
			// send the picture itself;
			logger.debug("找到的要下载的mask文件的名字为：" + maskfile.getAbsolutePath());
			String sentfilename = file.getName() + " " + maskfile.getName();
			// 发送两个文件的名字
			bytebufferwrite.clear();
			bytebufferwrite.put(FormatHandler.int2byte(sentfilename.length()));
			bytebufferwrite.put(sentfilename.getBytes("GB2312"));
			bytebufferwrite.flip();
			int sendlength = sc.write(bytebufferwrite);
			int correctlength = 4 + sentfilename.length();
			while (sendlength < correctlength) {
				sendlength += sc.write(bytebufferwrite);
			}

			buf_in = new BufferedInputStream(new FileInputStream(file));

			// 写文件大小
			bytebufferwrite.clear();
			bytebufferwrite.put(FormatHandler.long2byte(file.length()));
			bytebufferwrite.flip();
			sendlength = sc.write(bytebufferwrite);
			while (sendlength < 8) {
				sendlength += sc.write(bytebufferwrite);
			}
			byte[] buffer = new byte[buffersize];
			int len = -1;
			while ((len = buf_in.read(buffer, 0, buffer.length)) != -1) {
				bytebufferwrite.clear();
				bytebufferwrite.put(buffer, 0, len);
				bytebufferwrite.flip();
				int sendcount = sc.write(bytebufferwrite);
				while (sendcount < len) {
					sendcount += sc.write(bytebufferwrite);
				}
			}

			logger.debug("Send file " + file.getAbsolutePath()
					+ " Completed,Client name is:" + cl.getClientname());

			// send mask file
			buf_in = new BufferedInputStream(new FileInputStream(maskfile));
			bytebufferwrite.clear();
			bytebufferwrite.put(FormatHandler.long2byte(maskfile.length()));
			bytebufferwrite.flip();
			sendlength = sc.write(bytebufferwrite);
			while (sendlength < 8) {
				sendlength += sc.write(bytebufferwrite);
			}
			len = -1;
			while ((len = buf_in.read(buffer, 0, buffer.length)) != -1) {
				bytebufferwrite.clear();
				bytebufferwrite.put(buffer, 0, len);
				bytebufferwrite.flip();
				int sendcount = sc.write(bytebufferwrite);
				while (sendcount < len) {
					sendcount += sc.write(bytebufferwrite);
				}
			}
			logger.debug("Send mask file " + maskfilename.toString()
					+ " Completed,Client name is:" + cl.getClientname());
			// 记录本次已下载素材
			HashSet<String> temp = new HashSet<String>();
			if (cl.getThistimedownloaded().containsKey(directoryname)) {
				temp = cl.getThistimedownloaded().get(directoryname);
				temp.add(filenameonly);
				cl.getThistimedownloaded().put(directoryname, temp);
			} else {
				temp.add(filenameonly);
				cl.getThistimedownloaded().put(directoryname, temp);
			}

			buf_in.close();
			try {
				if (buf_in != null) {
					buf_in.close();
				}
			} catch (IOException e) {
				// 如果buf_in在关闭过程中发生IO异常，这里会捕捉到，但是即使加上了return语句也不会返回，会接着执行finally
				e.printStackTrace();
			}
		} 
		catch (FileNotFoundException e) {
			bytebufferwrite.clear();
			bytebufferwrite.put(FormatHandler.int2byte(0));
			bytebufferwrite.flip();
			sc.write(bytebufferwrite);
			logger.error("File Not Found In sendDonwloadFile(***) Function;Client name is"
					+ cl.getClientname());
			e.printStackTrace();
		} catch (NullPointerException e) {
			bytebufferwrite.clear();
			bytebufferwrite.put(FormatHandler.int2byte(0));
			bytebufferwrite.flip();
			sc.write(bytebufferwrite);
			logger.error("Corresponding Mask File Not Found In sendDonwloadFile(***) Function;Client name is"
					+ cl.getClientname());
			e.printStackTrace();
		} finally {
			// 读写锁释放读锁
			Main.xmlfilerwlock.readLock().unlock();
		}

		
		long endtime = System.nanoTime();
		logger.info("本次发送素材耗时：" + (endtime - starttime) / 1000 / 1000 + "毫秒");
	}

	/**
	 * 接收客户端自定义素材
	 * 
	 * @param bb
	 * @param sc
	 * @param cl
	 */
	private void saveUserMaterial(ByteBuffer bb, SocketChannel sc,
			ClientInformation cl) throws IOException {
		String clientname = sc.socket().getInetAddress().getHostAddress();
		String filename;
		int filenamelength;
		int filelength;
		byte[] buf = new byte[4];
		bb.get(buf, 0, 4);
		filenamelength = FormatHandler.byte2int(buf);
		logger.debug("客户端发送的文件名字大小为：" + filenamelength);
		if (filenamelength == 0) {
			logger.error("用户" + clientname + "发送的文件格式错误，文件名称长度为0；");
			return;
		}
		// 读取文件名字
		buf = new byte[filenamelength];
		bb.get(buf, 0, filenamelength);
		filename = new String(buf);
		logger.debug("客户端发送的文件名字为：" + filename);
		//计算文件应该保存的文件夹地址；
		int firstdirectory = HashLogic.StringHash(filename);
		int secondarydirectory = HashLogic.RSHash(filename);
		StringBuilder sb = new StringBuilder(ConfResolution.getClientDirectory());
		sb.append(firstdirectory).append("\\").append(secondarydirectory).append("\\");
		filename = sb.toString() + filename;
		logger.info("储存文件在："+filename);
		// 读文件大小
		buf = new byte[4];
		bb.get(buf, 0, 4);
		filelength = FormatHandler.byte2int(buf);
		logger.debug("客户端发送的文件大小为：" + filelength);
		FileHandler filep = new FileHandler();
		filep.readAndWrite0(bb, filename, filelength, sc);
		bb.clear();
		// 发送消息长度
		int reqlength = 6 + filenamelength;
		logger.info("接收用户上传文件" + new File(filename).getName() + "结束，向客户端发送反馈");
		bb.put(FormatHandler.int2byte(reqlength));
		StringBuilder reqstring = new StringBuilder("SAVED ").append(new File(
				filename).getName());
		bb.put(reqstring.toString().getBytes());
		bb.flip();
		sc.write(bb);
	}

	// echoBuffer,sc,cl
	/**
	 * 用户分享指令处理方法
	 * 
	 * @param bb
	 * @param sc
	 * @param cl
	 */
	private void shareMaterial(ByteBuffer bb, SocketChannel sc,
			ClientInformation cl) {
		byte[] buffer = new byte[4];
		bb.get(buffer, 0, 4);
		int infolength = FormatHandler.byte2int(buffer);
		logger.info("分享消息长度为：" + infolength);
		buffer = new byte[infolength];
		bb.get(buffer, 0, infolength);
		logger.info("接收的分享指令为：" + new String(buffer));
	}

	/**
	 * @author windy.zhu 退出交互方法； Server端通过统计Client端此次交互的行为，修改这个Client的资源XML文件；
	 *         将修改后的资源文件发回给Client；在发送xml文件的过程中应该是要加锁的，PC端逻辑需要加锁，移动端逻辑不需要；
	 * @param bb
	 * @param sc
	 * @param cl
	 * @throws IOException
	 * 
	 */
	private void exitProcedure(ByteBuffer bb, SocketChannel sc,
			ClientInformation cl) throws IOException {
		String updatemessage = cl.getupdatemessage();
		HashSet<String> thistimebrowsed = cl.getThistimebrowsed();
		HashMap<String, HashSet<String>> thistimedownloaded = cl
				.getThistimedownloaded();
		String clientname = cl.getClientname();
		String clientxmlfilepath = cl.getClientxmlfilepath();
		if (updatemessage == null) {
			logger.error("一般情况下不会出现这种情况，除非客户端和服务器端XML文件都是错误的，将向客户端发送0，退出结束过程；");
			bb.clear();
			bb.put(FormatHandler.int2byte(0));
			bb.flip();
			sc.write(bb);
			return;
		}
		BufferedInputStream buf_in = null;
		try {
			// 如果客户端不需要更新，则不发送更新的resouce.xml
			// 如果Client端没有进行任何素材文件夹的访问也没有进行任何下载，则不发送更新的resouce.xml
			if (thistimebrowsed.size() == 0 && thistimedownloaded.size() == 0) {
				logger.debug("There is no need for Client to update resource file because the user neither browse any directory nor download any material. "
						+ clientname);
				bb.clear();
				bb.put(FormatHandler.int2byte(0));
				bb.flip();
				int sendlength = 0;
				sendlength = sc.write(bb);
				while (sendlength < 4) {
					sendlength += sc.write(bb);
				}
				logger.debug("EXIT阶段向客户端发送0，结束，返回");
			} else {
				String updatedfilename = XmlHandler.updataXML(
						clientxmlfilepath, thistimebrowsed, thistimedownloaded);
				File file = new File(updatedfilename);
				logger.debug("Send Updated XML File to Client:" + clientname);
				bb.clear();
				bb.put(FormatHandler.int2byte((int) file.length()));
				bb.flip();
				sc.write(bb);
				buf_in = new BufferedInputStream(new FileInputStream(file));
				byte[] buffer = new byte[buffersize];
				int len = -1;
				while ((len = buf_in.read(buffer, 0, buffer.length)) != -1) {
					bb.clear();
					bb.put(buffer, 0, len);
					bb.flip();
					int sendcount = sc.write(bb);
					while (sendcount < len)
						sendcount += sc.write(bb);
				}
			}
		} catch (Exception e) {
			if (buf_in != null)
				buf_in.close();
		} finally {
			if (buf_in != null)
				buf_in.close();
		}
		return;
	}

	public void addclient(SocketChannel sc) {
		// logger.info("线程添加一个用户通道");
		// clientqueue.offer(sc);
		clientqueue.add(sc);
		client_count.getAndIncrement();
	}

	public int getClientPerThread() {
		synchronized (clientqueue) {
			return clientqueue.size();
		}
	}

	public boolean getCanAddClient() {
		return canaddclient;
	}
}

